package com.shenke.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shenke.user.common.ErrorCode;
import com.shenke.user.exception.BusinessException;
import com.shenke.user.model.dto.Event;
import com.shenke.user.model.dto.UserRegisterRequest;
import com.shenke.user.model.entity.User;
import com.shenke.user.model.enums.UserRoleEnum;
import com.shenke.user.model.vo.UserVO;
import com.shenke.user.service.PermissionService;
import com.shenke.user.service.UserService;
import com.shenke.user.mapper.UserMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.HttpServerErrorException;

import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.shenke.user.constant.UserConstant.*;

/**
* @author 86180
* @description 针对表【users】的数据库操作Service实现
* @createDate 2025-06-16 11:03:08
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService {

    @Resource
    private PermissionService permissionService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 用户注册
     * @param userRegisterRequest
     * @param idAddress
     * @return
     */
    @GlobalTransactional
    @Transactional(rollbackFor = HttpServerErrorException.InternalServerError.class)
    @Override
    public long userRegister(UserRegisterRequest userRegisterRequest, String idAddress) {
        log.info("用户注册开始，用户名：{}，手机号：{}，IP地址：{}", userRegisterRequest.getUsername(), userRegisterRequest.getPhone(), idAddress);
        // 校验参数
        String username = userRegisterRequest.getUsername();
        String password = userRegisterRequest.getPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        String email = userRegisterRequest.getEmail();
        String phone = userRegisterRequest.getPhone();
        // 校验密码
        if (StringUtils.isAnyBlank(password, checkPassword)) {
            log.warn("用户注册失败，密码参数为空，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
        }
        if (password.length() < 6 || checkPassword.length() < 6) {
            log.warn("用户注册失败，密码长度不符合要求，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于6位");
        }
        if (!password.equals(checkPassword)) {
            log.warn("用户注册失败，两次密码不一致，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        // 校验用户名、邮箱、手机号是否合法
        validateUsername(username);
        validateEmail(email);
        validatePhone(phone);
        // 密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((password + SALT).getBytes());
        // 插入数据
        synchronized (phone.intern()) {
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
            Long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                log.warn("用户注册失败，手机号已存在：{}", phone);
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已注册");
            }
            User user = new User();
            user.setUsername(username);
            user.setPassword(encryptPassword);
            user.setEmail(email);
            user.setPhone(phone);
            user.setGmtCreate(new Date(System.currentTimeMillis()));
            boolean result = this.save(user);
            if (!result) {
                log.error("用户注册失败，数据库插入失败，用户名：{}，手机号：{}", username, phone);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败");
            }
            // 绑定默认角色
            permissionService.bindDefaultRole(user.getUserId());
            // 异步发送注册消息
            Event event = new Event();
            event.setUserId(user.getUserId());
            event.setAction("REGISTER");
            event.setIp(idAddress);
            event.setDetail(JSON.toJSONString(user));
            // 生成消息唯一ID
            event.setMessageId("REG_" + user.getUserId() + "_" + System.currentTimeMillis());
            try {
                // 使用同步发送替代单向发送，确保消息发送成功
                SendResult sendResult = rocketMQTemplate.syncSend("operationLog", event);
                // 检查发送结果
                if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                    log.error("发送注册事件消息失败，用户ID：{}，发送状态：{}", 
                        user.getUserId(), sendResult.getSendStatus());
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册事件发送失败");
                }
                log.info("发送注册事件消息成功，消息ID：{}，事务ID：{}", 
                    event.getMessageId(), sendResult.getTransactionId());
            } catch (Exception e) {
                log.error("发送注册事件消息失败，用户ID：{}，错误：{}", user.getUserId(), e.getMessage(), e);
                // 事务回滚
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册事件发送失败");
            }
            
            log.info("用户注册成功，用户ID：{}，用户名：{}，手机号：{}", user.getUserId(), username, phone);
            return user.getUserId();
        }
    }

    /**
     * 用户登录
     * @param phone
     * @param password
     * @param idAddress
     * @return
     */
    @Override
    public UserVO userLogin(String phone, String password, String idAddress) {
        log.info("用户登录开始，手机号：{}，IP地址：{}", phone, idAddress);
        // 参数校验
        validatePhone(phone);
        // 校验密码
        if (StringUtils.isBlank(password)) {
            log.warn("用户登录失败，密码为空，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不能为空");
        }
        if (password.length() < 6) {
            log.warn("用户登录失败，密码长度不符合要求，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于6位");
        }
        // 密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((password + SALT).getBytes());
        // 查询用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);
        queryWrapper.eq(User::getPassword, encryptPassword);
        User user = this.getOne(queryWrapper);
        if (user == null) {
            log.warn("用户登录失败，用户不存在或密码错误，手机号：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        // 更新登录态，并将用户信息存入 session
        StpUtil.login(user.getUserId());
        StpUtil.getSession().set(USER_LOGIN_STATE, user);
        StpUtil.getSession().set(USER_LOGIN_IP, idAddress);
        log.info("用户登录成功，用户ID：{}，手机号：{}，IP地址：{}", user.getUserId(), phone, idAddress);
        return this.getUserVO(user);
    }

    /**
     * 分页用户列表
     * @Param loginId
     * @return
     */
    @Override
    public Page<UserVO> listUserVOByPage(long loginId) {
        log.info("分页查询用户列表，登录用户ID：{}", loginId);
        // 参数校验
        validateUserId(loginId);
        // 查询用户角色码
        String loginUserRoleCode = permissionService.getUserRoleCode(loginId);
        // 权限校验
        if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.UNDEFINED) {
            log.warn("分页查询用户列表失败，登录用户角色错误，用户ID：{}", loginId);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "登录用户角色错误");
        }
        List<Long> userIdList = new ArrayList<>();
        if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.USER) {
            userIdList.add(loginId);
        } else if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.ADMIN) {
            userIdList.addAll(permissionService.listUserByRoleCode(UserRoleEnum.USER.getValue()));
        } else if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.SUPER_ADMIN) {
            userIdList.addAll(permissionService.listUserByRoleCode(UserRoleEnum.USER.getValue()));
            userIdList.addAll(permissionService.listUserByRoleCode(UserRoleEnum.ADMIN.getValue()));
        }
        // 分页查询用户信息
        Page<UserVO> userPage = new Page<>(1, 10);
        List<UserVO> userVOList = this.listByIds(userIdList).stream()
                .map(this::getUserVO)
                .collect(Collectors.toList());
        userPage.setRecords(userVOList);
        userPage.setTotal(userVOList.size());
        log.info("分页查询用户列表成功，登录用户ID：{}，角色：{}，查询到用户数量：{}", loginId, loginUserRoleCode, userVOList.size());
        return userPage;
    }

    /**
     * 根据 id 获取脱敏用户信息
     * @param userId
     * @param loginId
     * @return
     */
    @Override
    public UserVO getUserVOById(long userId, long loginId) {
        log.info("查询用户信息，用户ID：{}，登录用户ID：{}", userId, loginId);
        // 参数校验
        validateUserId(userId);
        validateUserId(loginId);
        // 校验用户权限
        if (userId != loginId) {
            try {
                checkUserPermission(userId, loginId);
            } catch (BusinessException e) {
                log.warn("查询用户信息失败，权限校验不通过，用户ID：{}，登录用户ID：{}", userId, loginId);
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户权限不足");
            }
        }
        // 查询用户信息
        User user = this.getById(userId);
        if (user == null) {
            log.warn("查询用户信息失败，用户不存在，用户ID：{}", userId);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        }
        log.info("查询用户信息成功，用户ID：{}", userId);
        return getUserVO(user);
    }

    /**
     * 修改用户信息
     * @param userId
     * @param username
     * @param email
     * @param phone
     * @return
     */
    @Override
    public UserVO updateUser(long userId, String username, String email, String phone) {
        log.info("更新用户信息开始，用户ID：{}，用户名：{}，邮箱：{}，手机号：{}", userId, username, email, phone);
        // 校验参数
        validateUserId(userId);
        validateUsername(username);
        validateEmail(email);
        validatePhone(phone);
        // 获得当前登录用户 id
        Long loginId = Long.parseLong(StpUtil.getLoginId().toString());
        // 校验用户权限
        if (userId != loginId) {
            try {
                checkUserPermission(userId, loginId);
            } catch (BusinessException e) {
                log.warn("更新用户信息失败，权限校验不通过，用户ID：{}，登录用户ID：{}", userId, loginId);
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户权限不足");
            }
        }
        User user = new User();
        user.setUserId(userId);
        user.setUsername(username);
        user.setEmail(email);
        user.setPhone(phone);
        boolean res = this.updateById(user);
        if (!res) {
            log.error("更新用户信息失败，数据库更新失败，用户ID：{}", userId);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "修改用户信息失败");
        }
        log.info("更新用户信息成功，用户ID：{}", userId);
        return this.getUserVO(user);
    }

    /**
     * 用户重置密码
     *
     * @return
     */
    @Override
    public List<Long> userResetPwd(long loginId) {
        log.info("重置密码开始，登录用户ID：{}", loginId);
        // 校验参数
        validateUserId(loginId);
        // 获取用户角色码
        String userRoleCode = permissionService.getUserRoleCode(loginId);
        log.info("重置密码，登录用户角色：{}", userRoleCode);
        // 密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((DEFAULT_PASSWORD + SALT).getBytes());
        List<Long> failedList = new ArrayList<>();
        if (Objects.equals(userRoleCode, UserRoleEnum.USER.getValue())) {
            // 登录用户为普通用户
            User user = this.getById(loginId);
            user.setPassword(encryptPassword);
            if (!this.updateById(user)) {
                log.error("重置密码失败，用户ID：{}", user.getUserId());
                failedList.add(user.getUserId());
            }
        } else if (Objects.equals(userRoleCode, UserRoleEnum.ADMIN.getValue())) {
            // 登录用户为管理员
            List<Long> userIdList = permissionService.listUserByRoleCode(UserRoleEnum.USER.getValue());
            log.info("管理员重置密码，需要重置的用户数量：{}", userIdList.size());
            userIdList.forEach(userId -> {
                User user = this.getById(userId);
                if (user == null) {
                    log.error("重置密码失败，用户不存在，用户ID：{}", userId);
                    failedList.add(userId);
                    return;
                }
                user.setPassword(encryptPassword);
                if (!this.updateById(user)) {
                    log.error("重置密码失败，数据库更新失败，用户ID：{}", userId);
                    failedList.add(user.getUserId());
                }
            });
        } else if (Objects.equals(userRoleCode, UserRoleEnum.SUPER_ADMIN.getValue())) {
            // 登录用户为超级管理员
            List<User> userList = this.list();
            log.info("超级管理员重置密码，需要重置的用户数量：{}", userList.size());
            userList.forEach(user -> {
                user.setPassword(encryptPassword);
                if (!this.updateById(user)) {
                    log.error("重置密码失败，数据库更新失败，用户ID：{}", user.getUserId());
                    failedList.add(user.getUserId());
                }
            });
        } else {
            log.error("重置密码失败，用户角色异常，用户ID：{}，角色码：{}", loginId, userRoleCode);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户角色异常");
        }
        log.info("重置密码完成，登录用户ID：{}，失败数量：{}", loginId, failedList.size());
        return failedList;
    }

    /**
     * 权限校验
     * @param userId
     * @param loginId
     */
    private void checkUserPermission(Long userId, Long loginId) {
        log.debug("校验用户权限，用户ID：{}，登录用户ID：{}", userId, loginId);
        String userRoleCode = permissionService.getUserRoleCode(userId);
        String loginUserRoleCode = permissionService.getUserRoleCode(loginId);
        if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.UNDEFINED) {
            log.warn("校验用户权限失败，登录用户角色错误，用户ID：{}", loginId);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "登录用户角色错误");
        }
        if (UserRoleEnum.getEnumByValue(userRoleCode) == UserRoleEnum.UNDEFINED) {
            log.warn("校验用户权限失败，查询用户角色错误，用户ID：{}", userId);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "查询用户角色错误");
        }
        // 权限校验
        // 登录用户为普通用户，且查询用户不是自己
        if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.USER && !userId.equals(loginId)) {
            log.warn("校验用户权限失败，普通用户无权操作其他用户，查询用户ID：{}，登录用户ID：{}", userId, loginId);
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限");
        }
        // 登录用户为管理员且查询用户不是普通用户
        if (UserRoleEnum.getEnumByValue(loginUserRoleCode) == UserRoleEnum.ADMIN
                && UserRoleEnum.getEnumByValue(userRoleCode) != UserRoleEnum.USER) {
            log.warn("校验用户权限失败，管理员无权操作非普通用户，查询用户ID：{}，查询用户角色：{}，登录用户ID：{}", userId, userRoleCode, loginId);
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限");
        }
    }

    /**
     * 校验用户 id 是否合法
     * @param userId
     */
    private static void validateUserId(Long userId) {
        // 参数判断
        if (userId == null) {
            log.warn("校验用户ID失败，用户ID为空");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
        }
        if (userId < 0) {
            log.warn("校验用户ID失败，用户ID非法：{}", userId);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户 id 非法");
        }
    }

    /**
     * 校验手机号是否合法
     * @param phone
     */
    private static void validatePhone(String phone) {
        if (StringUtils.isBlank(phone)) {
            log.warn("校验手机号失败，手机号为空");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号不能为空");
        }
        // 校验手机号是否合法
        Pattern phonePattern = Pattern.compile(PHONE_REGEX);
        if (!phonePattern.matcher(phone).matches()) {
            log.warn("校验手机号失败，手机号格式不合法：{}", phone);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式不合法");
        }
    }

    /**
     * 校验邮箱是否合法
     * @param email
     */
    private static void validateEmail(String email) {
        if (StringUtils.isBlank(email)) {
            log.warn("校验邮箱失败，邮箱为空");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱不能为空");
        }
        // 校验邮箱是否合法
        Pattern emailPattern = Pattern.compile(EMAIL_REGEX);
        if (!emailPattern.matcher(email).matches()) {
            log.warn("校验邮箱失败，邮箱格式不合法：{}", email);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱格式不合法");
        }
    }

    /**
     * 校验用户名是否合法
     * @param username
     */
    private static void validateUsername(String username) {
        if (StringUtils.isBlank(username)) {
            log.warn("校验用户名失败，用户名为空");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名不能为空");
        }
        if (username.length() < 4 || username.length() > 20) {
            log.warn("校验用户名失败，用户名长度不符合要求：{}", username);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名长度在4-20之间");
        }
        // 校验用户名是否合法，允许的字符范围：字母、数字和下划线
        // 编译正则表达式
        Pattern pattern = Pattern.compile(NAME_REGEX);
        if (!pattern.matcher(username).matches()) {
            log.warn("校验用户名失败，用户名格式不合法：{}", username);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名只能包含字母、数字和下划线");
        }
    }

    /**
     * 返回脱敏用户信息
     * @param user
     * @return
     */
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        userVO.setUserId(user.getUserId());
        userVO.setUsername(user.getUsername());
        userVO.setEmail(user.getEmail());
        userVO.setPhone(user.getPhone());
        return userVO;
    }
}